package ioc

import (
	"gitee.com/huanminabc/go-utils/cmd"
	"gitee.com/huanminabc/go-utils/common"
	"gitee.com/huanminabc/go-utils/container"
	"gitee.com/huanminabc/go-utils/reflect_extend"
	"reflect"
)

func init() {
	//注册生命周期-程序关闭的时候调用
	cmd.StopHook(&lifecycleStop)
}

// key: 注入的名称没有, value: 注入的内容
var injectMap = make(map[string]InjectEntity)

// key: 返回值的类型, value: 注入的内容的指针
var typeInjectMap = make(map[string]*InjectEntity)

// 接口类型映射, 记录接口被实现的结构体关系
var interfaceInjectMap = make(map[string]*container.Set[*InjectEntity])

// 联系 key: 类型, value: 所有关联的注入
var relationInjectMap = make(map[string]*container.Set[*InjectEntity])

// 生命周期
var lifecycleStop = make([]func(), 0)

// 复用
var LifecycleGlobalPtr = &Lifecycle{}
var LifecycleGlobal = Lifecycle{}

// ioc 注册打印日志
var IocLog = false

// 生命周期,用于函数内部调用
type Lifecycle struct {
}

func (l *Lifecycle) AddStop(stop func()) {
	lifecycleStop = append(lifecycleStop, stop)
}

// Inject 注入
type InjectEntity struct {
	// 注入类型, 可以是函数, 结构体, 指针, 其他
	Type reflect.Kind
	// 注入的内容, 可以是函数, 结构体, 指针, 其他
	// 内部必须是指针类型
	Content interface{}
	//后期动态生成的指针类型的Content
	//内容必须是指针类型
	ContentPtr interface{} //xxx

	// 注入的名称
	// 如果是结构体,则是结构体的名称
	// 如果是函数,则是函数的名称
	// 如果是指针,则是指针的名称
	// 如果是其他,则是其他的名称
	Name string

	//返回类型 , 如果是函数,则需要设置
	ReturnType reflect.Type //这个是用于创建依赖函数的时候使用的

	IsNewFunc     bool //是否是New函数,如果是New函数,则函数的名称必须是'New*
	ExeRunNewFunc bool //是否执行过New函数,用于懒加载New函数

	//内部依赖注入完成
	InternalInject bool
}

func addInject(inject InjectEntity) {
	//判断是否重复注入函数
	if _, ok := injectMap[inject.Name]; ok {
		panic(inject.Name + "这个已经注册过了,不能重复注册")
	}

	//自己注入自己
	selfInject(inject)

	//注入容器
	injectMap[inject.Name] = inject
	//用于类型注入的时候使用,快速查找
	name := getTypeFullPath(inject.ReturnType)
	typeInjectMap[name] = &inject

	//结构字段反向关联
	relationInject(inject)

	structInjectInternal_add(inject)
}
func getTypeFullPath(t reflect.Type) string {
	return t.PkgPath() + "." + t.Name()
}

// 自己注入自己, 意思就是自己依赖自己 ,需要在结构体内部注入的时候就把自己注入
func selfInject(inject InjectEntity) {
	var t reflect.Type
	if inject.Type == reflect.Func && !inject.ExeRunNewFunc {
		t = inject.ReturnType
	} else {
		t = reflect.TypeOf(inject.Content).Elem()
	}
	//获取结构体内的全部字段
	fields := reflect_extend.GetStructFieldTypes(t)
	//然后遍历字段,查找是否有需要注入的字段,并且是自己的类型
	for _, field := range fields {
		//如果是指针,并且是结构体,并且是自己,则注入
		if field.Kind() == reflect.Ptr {
			//获取结构体的名称
			name := getTypeFullPath(field.Elem())
			path := getTypeFullPath(inject.ReturnType)
			//如果是自己,则注入
			if name == path {
				panic(name + "不能自己依赖自己,否则在深度拷贝的时候会出现死循环,解决办法手动注入")
			}
		}
	}
}

func relationInject(inject InjectEntity) {
	var typeNew reflect.Type
	//判断是否是函数
	if inject.Type == reflect.Func {
		typeNew = inject.ReturnType
	} else {
		typeNew = reflect.TypeOf(inject.Content).Elem()
	}
	//获取类型全部的字段
	fields := reflect_extend.GetStructFieldTypes(typeNew)
	//然后遍历字段,查找是否有需要注入的字段
	for _, field := range fields {
		//判断是否是结构体
		if field.Kind() == reflect.Struct {
			path := getTypeFullPath(field)
			if _, ok := relationInjectMap[path]; ok {
				relationInjectMap[path].Add(&inject)
			} else {
				set := container.NewSet[*InjectEntity]()
				set.Add(&inject)
				relationInjectMap[path] = set
			}
		}
	}
}

func getInject(name string) (InjectEntity, bool) {
	if inject, ok := typeInjectMap[name]; ok {
		//懒加载执行New函数
		exeNewFunc(inject)
		//结构体内部依赖注入
		structInjectInternal_get(inject)
		return *inject, true
	}
	return InjectEntity{}, false
}

func getInjectInternal(name string) (InjectEntity, bool) {
	//判断是否为空
	if name == "" {
		panic("name不能为空")
	}
	if inject, ok := typeInjectMap[name]; ok {
		//懒加载执行New函数
		exeNewFunc(inject)
		return *inject, true
	}
	return InjectEntity{}, false
}

// 懒加载执行New函数
func exeNewFunc(inject *InjectEntity) {
	if inject.IsNewFunc && !inject.ExeRunNewFunc {
		call := reflect.ValueOf(inject.Content).Call(nil)
		toInterface := reflect_extend.ValueToInterface(call)
		res := toInterface[0]
		//判断是否是指针,如果不是指针,则转换成指针
		if !reflect_extend.IsPtr(res) {
			res = reflect_extend.ToPtr(res)
		}
		inject.Content = res
		inject.ExeRunNewFunc = true
		//依赖这个类型的,需要重新注入
		path := getTypeFullPath(inject.ReturnType)
		structInjectInternal_(path, *inject)
	}
}

// 结构体内部依赖注入,只在查询的时候执行一次,不然影响性能,后期的注入会跟随添加的时候执行
func structInjectInternal_get(en *InjectEntity) {
	//判断内部依赖是否已经注入
	if en.InternalInject {
		return
	}
	//获取全部的字段
	fields := reflect_extend.GetStructFieldInfo(en.Content)
	//然后遍历字段,查找是否有需要注入的字段
	for _, field := range fields {
		//判断是否是结构体,并且字段是空的
		if field.Type.Kind() == reflect.Struct && field.Value.IsZero() {
			//获取结构体的名称
			name := getTypeFullPath(field.Type)
			//获取结构体的注入
			if inject, ok := getInjectInternal(name); ok {
				//如果目标字段不是指针,则需要取出指针内部的值
				rv := reflect.ValueOf(inject.Content).Elem()
				newValue := common.Copy(rv.Interface())
				field.Value.Set(reflect.ValueOf(newValue))
			}
		}
		if field.Type.Kind() == reflect.Ptr && field.Value.IsNil() {
			//获取结构体的名称
			name := getTypeFullPath(field.Type.Elem())
			//获取结构体的注入
			if inject, ok := getInjectInternal(name); ok {
				rv := reflect.ValueOf(inject.Content)
				field.Value.Set(rv)
			}
		}
	}
	checkRelationInject(en)
	en.InternalInject = true
}

// 添加的时候反向注入,判断是否有依赖这个类型的,如果有,则注入
func structInjectInternal_add(inject InjectEntity) {
	var typeNew reflect.Type
	//判断是否是函数,并且没有执行过New函数,那么就跳过
	if inject.Type == reflect.Func && !inject.ExeRunNewFunc {
		return
	} else {
		typeNew = reflect.TypeOf(inject.Content)
	}
	//根据最新注入的类型,找到所有依赖这个类型的,然后注入
	path := getTypeFullPath(typeNew.Elem())
	structInjectInternal_(path, inject)
}
func structInjectInternal_(typeNew string, inject InjectEntity) {
	//根据最新注入的类型,找到所有依赖这个类型的,然后注入
	if injects, ok := relationInjectMap[typeNew]; ok {
		for _, value := range injects.Elements() {
			//类型是函数并且没有执行过New函数,那么就跳过
			if value.Type == reflect.Func && !value.ExeRunNewFunc {
				continue
			}
			//获取结构体内的全部字段
			fields := reflect_extend.GetStructFieldInfo(value.Content)
			//判断是否有需要注入的字段,就是是结构体类型的字段并且是空的
			for _, field := range fields {
				path := getTypeFullPath(field.Type)
				if field.Type.Kind() == reflect.Struct && path == typeNew {
					//如果目标字段不是指针,则需要取出指针内部的值
					rv := reflect.ValueOf(inject.Content).Elem()
					newValue := common.Copy(rv.Interface())
					field.Value.Set(reflect.ValueOf(newValue))
				}
				if field.Type.Kind() == reflect.Ptr && field.Value.IsNil() && path == typeNew {
					rv := reflect.ValueOf(inject.Content)
					field.Value.Set(rv)
				}
			}
			checkRelationInject(value)
		}
	}
}

// 检查注入的情况,如果全部类型字段都注入完成,则删除
func checkRelationInject(value *InjectEntity) {
	//注入完成.开始检查,如果全部类型字段都注入完成,则删除
	//统计字段是空的数量
	nilCount := 0
	field := reflect_extend.GetStructFieldInfo(value.Content)
	for _, v := range field {

		if v.Type.Kind() == reflect.Struct && v.Field.Tag.Get("inject") != "not" && v.Value.IsZero() {
			nilCount++
		}
		if v.Type.Kind() == reflect.Ptr && v.Type.Elem().Kind() == reflect.Struct && v.Field.Tag.Get("inject") != "not" && v.Value.IsNil() {
			nilCount++
		}
	}
	//判断是否全部注入完成,那么就把这个注入删除,不然占用内存
	if nilCount == 0 {
		//找到存储这个类型的位置, 然后删除标记
		for _, delfield := range field {
			var name string
			if delfield.Type.Kind() == reflect.Struct {
				name = getTypeFullPath(delfield.Type)
			}
			if delfield.Type.Kind() == reflect.Ptr && delfield.Type.Elem().Kind() == reflect.Struct {
				name = getTypeFullPath(delfield.Type.Elem())
			}
			//如果 name 是空
			if name == "" {
				continue
			}
			if key, ok := relationInjectMap[name]; ok {
				key.RemoveByField(func(v *InjectEntity) bool {
					return v.Name == value.Name
				})
			}
		}

	}
}

// 获取typeInjectMap的全部类型 ,返回的内容都输指针
func GetTypeInjectMap() []*InjectEntity {
	var res []*InjectEntity
	for _, v := range typeInjectMap {
		//跳过没有处理的函数
		if v.Type == reflect.Func && !v.ExeRunNewFunc {
			continue
		}
		res = append(res, v)
	}
	return res
}
